<template>
  <div class="login-bg">
    <div class="locale">
      <el-dropdown trigger="hover" @command="handleLocaleChanged">
        <el-icon :size="24">
          <ico-svg icon="global-line"></ico-svg>
        </el-icon>
        <template #dropdown>
          <el-dropdown-menu>
            <el-dropdown-item command="zh_CN" v-if="locale == 'zh_CN'"
              >简体中文 &nbsp;
              <el-icon :size="16"><Check /></el-icon>
            </el-dropdown-item>
            <el-dropdown-item command="zh_CN" v-if="locale == 'en_US'"
              >简体中文
            </el-dropdown-item>
            <el-dropdown-item command="en_US" v-if="locale == 'zh_CN'"
              >English
            </el-dropdown-item>
            <el-dropdown-item command="en_US" v-if="locale == 'en_US'"
              >English &nbsp; <el-icon :size="16"><Check /></el-icon
            ></el-dropdown-item>
          </el-dropdown-menu>
        </template>
      </el-dropdown>
    </div>
    <el-card shadow="always" class="box-card login-card">
      <el-form ref="formRef" :model="formEdit" :rules="rules" label-width="0">
        <el-row>
          <el-col :lg="14" :md="14" :sm="24" :xs="24" class="login-logo">
          </el-col>
          <el-col :lg="10" :md="10" :sm="24" :xs="24">
            <div class="login-form">
              <h1>{{ env.VITE_APP_LOGIN_TITLE }}</h1>
              <div style="margin: 20px 0"></div>
              <el-alert
                v-if="showErrorMessage"
                :title="errorMessage"
                @close="onErrorMessageClose"
                type="error"
                effect="dark"
              />
              <div style="margin: 20px 0"></div>

              <!-- <button @click="onShow('blockPuzzle')">滑块</button>
  <button @click="onShow('clickWord')">点击文字</button> -->
              <Verify
                mode="pop"
                captchaType="blockPuzzle"
                :imgSize="{ width: '400px', height: '200px' }"
                ref="verify"
              ></Verify>

              <el-form-item prop="account">
                <el-input
                  ref="accountRef"
                  v-model="formEdit.account"
                  maxlength="30"
                  size="large"
                  clearable
                  :placeholder="t('login.accountPlaceholder')"
                  @keyup.enter="handleAccountKeyEnter"
                >
                </el-input>
              </el-form-item>

              <el-form-item prop="password">
                <el-input
                  ref="passwordRef"
                  v-model="formEdit.password"
                  maxlength="30"
                  size="large"
                  :type="type"
                  @focus="type = 'password'"
                  autocomplete="new-password"
                  show-password
                  clearable
                  :placeholder="t('login.passwordPlaceholder')"
                  @keyup.enter.native="handlePasswordKeyEnter"
                >
                </el-input>
              </el-form-item>

              <el-form-item prop="captcha" ref="formItemCaptchaRef">
                <el-input
                  ref="captchaRef"
                  v-model="formEdit.captcha"
                  maxlength="4"
                  size="large"
                  clearable
                  :placeholder="t('login.captchaPlaceholder')"
                  class="fix-el-input-group-append"
                  @keyup.enter.native="handleCaptchaKeyEnter"
                >
                  <template #append>
                    <el-image
                      style="
                        width: 100px;
                        height: 40px;
                        margin-left: 4px;
                        cursor: pointer;
                        border: 1px solid var(--el-border-color-light);
                      "
                      :src="captchaUrl"
                      fit="fill"
                      @click="handleCaptchaRefresh"
                    />
                  </template>
                </el-input>
              </el-form-item>
              <div style="margin: 20px 0"></div>

              <el-button
                type="primary"
                size="large"
                :loading="loading"
                style="width: 100%"
                @click="onLogin"
                >{{ t("login.loginButton") }}
              </el-button>
              <div class="signup">
                <el-link :underline="false" href="#/forgot-password">{{
                  t("login.forgotPassword")
                }}</el-link>
                <!-- <el-link :underline="false" href="#/signup">{{
                  t("signup.createAccount")
                }}</el-link> -->
              </div>
              <br />
              <br />
              <br />
            </div>
            <div class="login-notice">
              <label>
                <el-popover
                  width="200"
                  :content="t('login.check')"
                  placement="top"
                  effect="dark"
                  :visible="isAgreeTip"
                >
                  <template #reference>
                    <input
                      ref="agreeRef"
                      type="checkbox"
                      v-model="iHaveReadAndAccepted"
                    />
                  </template>
                </el-popover>
                {{ t("common.read") }}
              </label>

              <a :href="registration" target="_blank">
                {{ t("common.registration") }}
              </a>
              <a :href="copyright" target="_blank">
                {{ t("common.copyright") }}
              </a>
              <a href="#"> {{ t("common.privacy") }} </a>
              <!-- <el-checkbox v-model="agree"> 我已阅读并接受 </el-checkbox>
            <label class="el-checkbox">
              <el-link type="primary">《注册声明》</el-link></label
            >
            <el-link type="primary">《版权声明》</el-link>
            <el-link type="primary">《隐私政策》</el-link> -->
            </div>
          </el-col>
        </el-row>
      </el-form>
    </el-card>
  </div>
</template>
<script setup lang="ts">
import { reactive, ref, computed, onMounted, onActivated } from "vue";
import { useRouter } from "vue-router";
import { Check } from "@element-plus/icons-vue";
import moment from "moment";
import utils from "../../utils/utils";
import { useStore } from "../../stores/store";
import apiAuth from "./api/auth";
// import adminRouters from "../../../mock/api/admin/routers";
import demoRouters from "../../../mock/api/demo/_routers";
// import lockerRouters from "../../../mock/api/locker/routers";
import { addDynamicRouters, clearDynamicRouters } from "../../utils/router";
import { useI18n } from "vue-i18n";
const { t } = useI18n({ useScope: "global" });
import i18n from "../../i18n";
import apiPortal from "./api/portal";

const store = useStore();
const env = import.meta.env;
// 图标样式
const router = useRouter();
const formRef = ref();
const formItemCaptchaRef = ref();
const accountRef = ref();
const passwordRef = ref();
const captchaRef = ref();
const agreeRef = ref();
const isAgree = ref(true);
const isAgreeTip = ref(false);
const showErrorMessage = ref(false);
// 前台生成 uuid，用于后台持久化图形验证码url、私钥；前台根据 uuid 获取的公钥保存在 sessionStorage，公钥用于加密密码
let publicKeySessionId = ref();
const captchaUrl = ref("");
const errorMessage = ref("");
const type = ref("");
const loading = ref(false);
const locale = ref("");
const registration = ref("");
const copyright = ref("");
// 我同意
const iHaveReadAndAccepted = ref(false);

const formEdit = reactive({
  account: "",
  password: "",
  captcha: "",
  sessionId: "",
});

const rules = reactive({
  account: [
    {
      required: true,
      message: () => t("common.required"),
      trigger: "change",
    },
  ],
  passwordOriginal: [
    {
      required: true,
      message: () => t("common.required"),
      trigger: "change",
    },
  ],
  captcha: [
    {
      required: true,
      message: () => t("common.required"),
      trigger: "change",
    },
  ],
});

onMounted(() => {
  // if (window.store == undefined) {
  //   window.store = store;
  // }

  if (env.VITE_APP_PROFILES == "development") {
    formEdit.account = "summer-admin";
    formEdit.password = "SummerAdmin#2023";
  }

  // 初始化语言选项
  locale.value = store.appLocale;
  setTimeout(() => {
    accountRef.value.focus();
    // msgRef.value.$el.style.display = "none";
  }, 100);
  copyrightChanged(locale.value);

  // 还原我接受状态
  const accepted = store.localStorage("iHaveReadAndAccepted");
  // console.log("accepted", accepted);
  if (accepted) {
    iHaveReadAndAccepted.value = true;
  }
  // console.log("login onMounted", locale);
  // publicKeySessionId.value = store.session("publicKeySessionId");
  // publicKeyExpireTime.value = store.session("publicKeyExpireTime");

  // publicKeySessionId.value = "";
  // console.log("onMounted1", publicKeySessionId);
  getPublicKeySessionId();
  // console.log("onMounted2", publicKeySessionId);
  // handleCaptchaRefresh();

  // 30分钟后刷新页面，防止登录时 sessionId 超时过期，后台报错 SessionId empty value anomaly
  timerGetPublicKeySessionId = setInterval(() => {
    getPublicKeySessionId();
    // handleCaptchaRefresh();
  }, 1000 * 60 * 25);
});

// 获取有效 id 同时刷新图形验证码 CaptchaRefresh，sessionId 有效期30分钟
let timerGetPublicKeySessionId: any = null;
const getPublicKeySessionId = () => {
  // if (publicKeySessionId.value == null) {
  let uuid = utils.getUUID();
  // console.log("publicKeySessionId", uuid);
  apiAuth
    .generateSessionPublicKeyQ(uuid)
    .then((response: any) => {
      if (response.data.code == 200) {
        publicKeySessionId.value = uuid;
        // 默认2小时过期
        // publicKeyExpireTime.value = utils.moment2DateTimeString(
        //   moment().add(2, "hours"),
        //   "YYYY-MM-DD HH:mm:ss"
        // );
        // // console.log("env.VITE_APP_PROFILES", env.VITE_APP_PROFILES);
        // if (env.VITE_APP_PROFILES == "development") {
        //   // 开发环境2分钟过期 minutes
        //   publicKeyExpireTime.value = utils.moment2DateTimeString(
        //     moment().add(20, "seconds"),
        //     "YYYY-MM-DD HH:mm:ss"
        //   );
        // }
        // 存储会话id，用会话id第二次存储公钥
        // store.setSession("publicKeySessionId", publicKeySessionId.value);
        //// 公钥Q长度 130
        store.setSession(publicKeySessionId.value, response.data.data);
        // store.setSession("publicKeyExpireTime", publicKeyExpireTime.value);

        // 获取到新 key 重新刷新验证码
        // console.log("getPublicKeySessionId");
        handleCaptchaRefresh();
      } else {
        showErrorMessage.value = true;
        errorMessage.value = response.data.msg;
      }
    })
    .catch((err: any) => {
      utils.errorNotification(t("common.submitException"), err);
    });
  // }
  // else {
  //   // 可能是从缓存中读取的
  //   let expireTime = utils.dateTimeString2Moment(
  //     publicKeyExpireTime.value,
  //     "YYYY-MM-DD HH:mm:ss"
  //   );
  //   // 如果前面的时间大于后面的时间，返回大于的正整数秒数
  //   let diff = moment().diff(expireTime, "seconds");
  //   if (diff > 0) {
  //     // 已过期，触发重新请求公钥
  //     publicKeySessionId.value = null;
  //     store.removeSession("publicKeySessionId");
  //     getPublicKeySessionId();
  //   }
  //   // console.log("login diff:", diff);
  // }
};

const onErrorMessageClose = () => {
  showErrorMessage.value = false;
  // msgRef.value.$el.style.display = "none";
};

const handleAccountKeyEnter = () => {
  if (formEdit.account.length > 0) {
    passwordRef.value.focus();
  }
};

const handlePasswordKeyEnter = () => {
  if (formEdit.password.length > 0) {
    captchaRef.value.focus();
  }
};

const handleCaptchaKeyEnter = () => {
  if (formEdit.captcha.length > 0) {
    onLogin();
  }
};

const handleCaptchaRefresh = () => {
  // console.log("handleCaptchaRefresh", publicKeySessionId.value);
  // if (publicKeySessionId.value) {
  captchaUrl.value = apiAuth.generateCaptchaUrl(publicKeySessionId.value);
  // console.log("handleCaptchaRefresh", captchaUrl.value);
  captchaRef.value.clear();
  captchaRef.value.focus();
  // formEdit.captcha = "";
  formItemCaptchaRef.value.resetField();
  // }
};

const onLogin = async () => {
  // if (!isAgree.value) {
  //   isAgreeTip.value = true;
  //   return;
  // }
  if (!iHaveReadAndAccepted.value) {
    isAgreeTip.value = true;
    return;
  }
  store.setLocalStorage("iHaveReadAndAccepted", true);

  // 清理路由数据，补偿没有点击注销的情况
  clearDynamicRouters();
  // console.log("onLogin", msgRef.value.$el.innerHtml);
  // let validate = false;
  // await formRef.value.validate((valid: any, fields: any) => {
  //   validate = valid;
  // });
  // if (!validate) return;
  await formRef.value.validate();

  // 密码加密
  let pubKey = store.session(publicKeySessionId.value);
  if (pubKey == null) {
    showErrorMessage.value = true;
    errorMessage.value = t("login.pubKeyNull");
    return;
  }
  let password = utils.doSm2Encrypt(pubKey || "", formEdit.password);

  // 防止密码重复加密
  let obj = {
    account: formEdit.account,
    password: password,
    captcha: formEdit.captcha,
    sessionId: publicKeySessionId.value,
  };

  loading.value = true;
  apiAuth
    .loginByAccount(obj)
    .then((response: any) => {
      // if (response != undefined) 当后台接口不可用时返回 undefined，
      // 统一被包装类 request.ts 拦截，并示信息 The server is not responding...
      handleCaptchaRefresh();

      if (response.data.code == 200) {
        // 缓存当前登录账号信息
        if (response.data.data.roleList == undefined) {
          utils.warningNotification(t("login.notConfiguredRole"));
          loading.value = false;
          return;
        }
        if (response.data.data.tokenInfo == undefined) {
          utils.warningNotification(t("login.noValidToken"));
          loading.value = false;
          return;
        }
        let roleNames = "";
        response.data.data.roleList.forEach((m) => {
          roleNames += m.roleName + ",";
        });
        if (roleNames.length > 0) {
          roleNames = roleNames.substring(0, roleNames.length - 1);
        }
        let accountInfo = {
          accountId: response.data.data.account.accountId,
          account: response.data.data.account.account,
          avatarUrl: response.data.data.avatarUrl,
          name: response.data.data.account.name,
          gender: response.data.data.account.gender,
          employeeNumber: response.data.data.account.employeeNumber,
          orgCode: response.data.data.account.orgCode,
          orgName: response.data.data.account.orgName,
          deptCode: response.data.data.account.deptCode,
          deptName: response.data.data.account.deptName,
          roleNames: roleNames,
        };
        store.setAccountName(response.data.data.account.name);
        store.setAccountInfo(accountInfo);
        store.setAccountRoles(response.data.data.roleList);
        store.setTokenInfo(response.data.data.tokenInfo);

        // 初始化路由
        apiPortal
          .loadMenuItem("MENU_SYS")
          .then((response: any) => {
            if (response.data.code == 200) {
              // todo 临时加载本地菜单
              // tempRouters.forEach((m, index) => {
              //   if (index == 0) {
              //     response.data.data.unshift(0, m);
              //   } else {
              //     response.data.data.push(m);
              //   }
              // });
              if (
                response.data.data == undefined ||
                response.data.data.length == 0
              ) {
                utils.warningNotification(t("login.notConfiguredMenu"));
                loading.value = false;
                return;
              }

              // 临时加载本地 demo 菜单
              let demoEnabled = utils.convertToBoolean(
                <string>(<unknown>env.VITE_DEMO_ENABLED)
              );
              if (demoEnabled) {
                demoRouters.forEach((m, index) => {
                  response.data.data.push(m);
                });
              }

              // 动态添加路由
              addDynamicRouters(response.data.data);
              // console.log("response.data.data", response.data.data);

              // 缓存路由数据，所以，如果路由数据修改了，必须重新登录才能生效
              store.setMenuItems(response.data.data);

              for (let i = 0; i < store.menuItems.length; i++) {
                if (store.menuItems[i].hasChildren == 0) {
                  // 登录成功，延迟进入主页，给动态添加路由反映时间，某些时候路由跳转报错，不好复现问题
                  let url = store.menuItems[i].uri;
                  // console.log("first url", url);

                  clearTimeout(timerGetPublicKeySessionId);

                  setTimeout(() => {
                    router.push({
                      // name: env.VITE_APP_HOME_ROUTER_NAME,
                      name: url,
                      params: {},
                    });
                  }, 1000);
                  break;
                }
              }
            }
          })
          .catch((err: any) => {
            utils.errorNotification(t("common.submitException"), err);
            loading.value = false;
          });
      } else {
        loading.value = false;
        showErrorMessage.value = true;
        errorMessage.value = response.data.msg;
        if (response.data.msg.indexOf("sessionId") > -1) {
          // sessionId 空值异常
          // 由于服务器端重置数据，找不到对应的 sessionId，一般仅在开发环境出现此错误
          errorMessage.value = t("login.sessionTimeOut");
          publicKeySessionId.value = null;
          getPublicKeySessionId();
        }
      }
    })
    .catch((err: any) => {
      formEdit.captcha = "";
      loading.value = false;
      utils.errorNotification(t("common.submitException"), err);
    });
};

const copyrightChanged = (command: string) => {
  // 注册声明、版权声明
  if ("zh_CN" == command) {
    registration.value = env.VITE_BASE + "registration_cn.html";
    copyright.value = env.VITE_BASE + "copyright_cn.html";
  } else {
    registration.value = env.VITE_BASE + "registration_en.html";
    copyright.value = env.VITE_BASE + "copyright_en.html";
  }
};

const handleLocaleChanged = (command: string) => {
  // console.log("handleLocaleChanged", command);
  locale.value = command;
  i18n.global.locale.value = command;
  store.setAppLocale(command);
  copyrightChanged(command);

  // 重置表单校验
  if (formRef.value) {
    formRef.value.resetFields();
  }
};
</script>
<style lang="scss" scoped>
.login-bg {
  height: 100%;
  width: 100%;
  display: block;
  overflow: hidden !important;
  /* fix 禁止出现滚动条 */
  position: fixed;
}
.locale {
  top: 20px;
  right: 60px;
  position: absolute;
  display: block;
  cursor: default;
}
.login-bg::before {
  content: "";
  position: absolute;
  z-index: -1;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  -webkit-filter: $login-bg-filter-blur;
  -moz-filter: $login-bg-filter-blur;
  -ms-filter: $login-bg-filter-blur;
  filter: $login-bg-filter-blur;
  /* fix 白边，去掉白边出现滚动条 */
  margin: -50px;
  //   background-image: url("https://img-qn.51miz.com/2017/03/26/21/2017032621720647_P213012_a40f5760.jpeg!/quality/90/unsharp/true/compress/true/fwfh/830x400");
  //   background-image: url("https://img-qn.51miz.com/2017/03/26/21/2017032621709684_P213266_cda09ee4.jpeg!/quality/90/unsharp/true/compress/true/fw/900");
  //   background-image: url("https://img-qn.51miz.com/preview/photo/00/01/51/46/P-1514665-BFD743FF.jpg!/quality/90/unsharp/true/compress/true/fwfh/830x400");
  //   background-image: url("https://img-qn.51miz.com/preview/photo/00/01/54/87/P-1548767-B4DD056A.jpg!/quality/90/unsharp/true/compress/true/fwfh/830x400");
  //   background-image: url("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpmtc65a49.pic20.websiteonline.cn%2Fupload%2F7fc531f98ce76d3274b8db3fcd75fcde_uwsz.jpeg&refer=http%3A%2F%2Fpmtc65a49.pic20.websiteonline.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1649574693&t=1d96a4193037c430b35e5658042d46d5");
  //   background-image: url("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fwww.xinhuanet.com%2Fbook%2F2020-07%2F20%2F139225694_15952117732871n.jpg&refer=http%3A%2F%2Fwww.xinhuanet.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1649577156&t=b84399f704c431c16ecb62da33bae8f3");
  //   background-image: url("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg9.51tietu.net%2Fpic%2F2019-091122%2Fwh4ixl0qwwywh4ixl0qwwy.jpg&refer=http%3A%2F%2Fimg9.51tietu.net&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1649578369&t=39050d2ac16fb237bc64b91a6df94e36");
  // background-image: url("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic1.zhimg.com%2Fv2-a3b927b4ec290517619301ef72d86f4e_1440w.jpg%3Fsource%3D172ae18b&refer=http%3A%2F%2Fpic1.zhimg.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1649574778&t=c9035ed9f8d5ecf54b7465a9f952bc41");
  //   background-image: url("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic1.win4000.com%2Fwallpaper%2F2018-11-20%2F5bf3f1badc437.jpg%3Fdown&refer=http%3A%2F%2Fpic1.win4000.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1649578785&t=7f43a7837bc954995840c6c9f7251e3d");
  background-color: #f0f2f5;
  background-position: center center;
  background-size: cover;
}

.login-card {
  width: 950px;
  left: calc(50% - 950px / 2);
  top: calc(100% / 2 - 250px);
  position: absolute;
  z-index: 100;
  border-radius: 0;

  .el-row {
    padding: 0;
    margin: 0;
  }

  .el-col {
    padding: 0;
    margin: 0;
  }
}

.login-logo {
  // background-image: url("../../assets/images/login-newlogo.jpg");
  background-image: url("../../assets/images/login-logo.jpg");
  background-position: center center;
  background-size: 100% 100%;
}

.login-form {
  padding: 30px 60px;
}

.login-form h1 {
  text-align: center;
  font-weight: normal;
  margin-bottom: 50px;
  // color: var(--el-color-primary-light-1);
  color: #4b9ff8;
}

.login-notice {
  display: block;
  background-color: #fafafa;
  color: var(--el-color-info);
  font-size: 12px;
  text-align: center;
  padding: 15px;
  position: relative;
  bottom: 0;

  input {
    vertical-align: top;
  }

  a {
    text-decoration: none;
    color: var(--el-color-info);
  }
}
</style>
<style>
/* 图形验证码 */
.fix-el-input-group-append .el-input-group__append,
.fix-el-input-group-append .el-input-group__prepend {
  padding: 0;
  background-color: #fff;
  border: 0;
  box-shadow: none;
  border-radius: 0;
}

.login-card .el-card__body {
  padding: 0;
}

.login-card .el-input {
  --el-input-border-radius: 0;
}

.signup {
  display: flex;
  justify-content: space-between;
}
</style>
